#include "core/engine.h"
#include "core/system.h"

namespace core {
	Engine::Engine():
		mConfig(new Config()),
		mInput(new input::Input()),
		mVideo(new video::Video()),
		mClock(new core::Clock())
	{
		add(mInput);
		add(mVideo);
		add(mClock);
	}

	Engine::~Engine() {
	}

	void Engine::run() {
		for (SystemMap::const_iterator it = mSystemMap.begin(); it != mSystemMap.end(); ++it)
			mConfig->settings().add(it->second->mSettings);

		std::cout << "loading assault.cfg... "; 
		mConfig->load("assault.cfg");
		std::cout << "done." << std::endl;

		initializeSystems();
		mTaskManager.start();
		shutdownSystems();
	}

	void Engine::stop() {
		events().broadcast(TaskManager::StopEvent());
	}

	boost::program_options::options_description& Engine::settings() {
		return mConfig->settings();
	}

	void Engine::add(Engine::SystemPtr system) {
		if (mSystemMap.find(system->getName()) == mSystemMap.end()) {
			if (system->mSystemUpdater.get())
				mTaskManager.add(system->mSystemUpdater);
			mSystemMap.insert(std::make_pair(system->getName(), system));
		}
		else
			gLog << "Engine::add - System already added: " << system->getName();
	}

	bool Engine::remove(Engine::SystemPtr system) {
		SystemMap::iterator it = mSystemMap.find(system->getName());
		if (it == mSystemMap.end()) {
			gLog << "Engine::remove - Cannot find system: " << system->getName();
			return false;
		}

		mSystemMap.erase(it);
		return true;
	}

	Engine::SystemPtr Engine::get(const std::string& name) const {
		SystemMap::const_iterator it = mSystemMap.find(name);
		
		if (it == mSystemMap.end()) {
			gLog << "Engine::get - Cannot find system: " << name;
			return SystemPtr();
		}

		return it->second;
	}

	TaskManager& Engine::getTaskManager() {
		return mTaskManager;
	}	

	void Engine::initializeSystems() {
		for (SystemMap::iterator it = mSystemMap.begin(); it != mSystemMap.end(); ++it) {
			gLog.info() << "Initializing " << it->second->getName();
			it->second->init();
		}

		if (mApplication.get()) {
			gLog.info() << "Initializing app: " << mApplication->getName();
			mApplication->init();
			
			add(mApplication);
		}
	}

	void Engine::shutdownSystems() {
		if (mApplication.get()) {
			gLog.info() << "Shutting down app: " << mApplication->getName();
			mApplication->shutdown();

			mApplication.reset();
		}

		for (SystemMap::iterator it = mSystemMap.begin(); it != mSystemMap.end(); ++it) {
			gLog.info() << "Shutting down " << it->second->getName();
			it->second->shutdown();
		}
	}

	const boost::shared_ptr<Config>& Engine::config() {
		return mConfig;
	}

	const boost::shared_ptr<video::Video>& Engine::video() {
		return mVideo;
	}

	const boost::shared_ptr<input::Input>& Engine::input() {
		return mInput;
	}

	const boost::shared_ptr<core::Clock>& Engine::clock() {
		return mClock;
	}

	EventChannel& Engine::events() {
		return mChannel;
	}

	void Engine::setApplication(const boost::shared_ptr<app::Application>& application) {
		if (mApplication.get() == NULL) {
			mApplication = application;
			
			mApplication->mEngine = this;
		}
		else
			gLog << "Engine:setApplication - application already set, ignoring " << application->getName();
	}
}
